/* Vue框架-特定文件检测策略 */
const { ESLint } = require('eslint');

const compiler = require('vue-template-compiler');
const Fs = require('@lib/utils/fs');
const CONSTS = require('@lib/consts/index');
const Report = require('@lib/utils/report');
const {
  isValuableArray,
  mergeArrayByField,
  getContentSize,
  bytes2KB,
  escapeHTML
} = require('@lib/utils/tools');

// 需要写入报告文件的数据
const outputList = [];

class FwVue {
  /**
   * 执行策略的函数
   * @param {*} vueList Vue框架特定文件列表
   * @param {*} extra 额外参数
   * @param {*} cb 回调函数
   */
  static async execute(vueList = [], extra, cb) {
    try {
      if (!isValuableArray(vueList)) {
        cb && cb();
        return;
      }
      // 1.按照文件遍历
      for (const item of vueList) {
        const { filePath } = item;
        const content = Fs.readFileSync(filePath);
        // 2.策略开始
        FwVue.detectFileSize(item, content, outputList);
        // 3.其余策略均基于 eslint、vue-eslint-parser、eslint-plugin-vue 规则检测
        await FwVue.eslintContentStrategy(item, outputList);
      }
      // 4.整合outputList相同title项
      const newOutputList = mergeArrayByField(outputList, 'title', 'data');
      // 5.输出数据到报告目录下的临时文件
      if (!newOutputList.length) {
        cb && cb();
        return;
      }
      const { index } = extra;
      Report.outputTempFile(
        newOutputList,
        'Vue框架.vue文件',
        `${index}fw-vue-${CONSTS.REPORT_TEMP_FILENAME}`,
        () => {
          cb && cb();
        }
      );
    } catch (e) {
      console.error(`执行Vue框架-特定文件检测策略报错：${e}`);
      cb && cb();
    }
  }

  /**
   * 策略1：检测vue文件是否过大，超过阈值则给出告警提示
   * @param {*} vueItem vue文件列表每一项
   * @param {*} content vue文件内容
   * @param {*} outputList 报告文件的数据列表
   */
  static detectFileSize(vueItem, content, outputList = []) {
    const { script, template, styles } = compiler.parseComponent(content);
    // console.log(script, template, styles);
    const scriptSize = script ? getContentSize(script.content) : 0;
    const templateSize = template ? getContentSize(template.content) : 0;
    const stylesSize = isValuableArray(styles) ? getContentSize(styles[0].content) : 0;
    // console.log(scriptSize, templateSize, stylesSize);
    const totalSize = bytes2KB(scriptSize + templateSize + stylesSize);
    const limit = CONSTS.DEFAULT_VUE_FILE_SIZE_LIMIT;
    if (totalSize > limit) {
      Report.formatAndpushReportData(
        [{
          fileName: vueItem.fileName,
          filePath: vueItem.filePath,
          size: `${vueItem.size}KB`
        }],
        `文件大小超过阈值${limit}KB，建议拆分成多个组件实现按需加载`,
        outputList
      );
    }
  }

  /**
   * 其余策略：基于eslint、vue-eslint-parser、eslint-plugin-vue 规则检测，
   * 详见CONSTS.VUE_ESLINT_RULES常量
   * @param {*} vueItem vue文件列表每一项
   * @param {*} outputList 报告文件的数据列表
   */
  static async eslintContentStrategy(vueItem, outputList = []) {
    // 1.初始化 ESLint 实例
    const rules = CONSTS.VUE_ESLINT_RULES;
    const newConfig = {
      ...CONSTS.VUE_ESLINTRC_CONFIG
    };
    // 规则整合进eslint配置
    rules.forEach(ruleItem => {
      newConfig.overrideConfig.rules[ruleItem.ruleId] = ruleItem.ruleVal;
    });
    // console.log(`newConfig: ${JSON.stringify(newConfig)}`);
    const eslintIns = new ESLint(newConfig);
    // 2.执行检测
    const { filePath } = vueItem;
    const results = await eslintIns.lintFiles([filePath]);
    // const formatter = await eslintIns.loadFormatter('stylish');
    // // 控制台可以打印所有告警和错误的检测结果
    // const resultText = formatter.format(results);
    // console.log(resultText);
    // 3.归类数据
    // console.log(`results: ${results}`);
    // 检测结果数组存在多个元素，每个result对应一个文件的检测结果
    if (isValuableArray(results)) {
      results.forEach(resultItem => {
        const { messages } = resultItem;
        // console.log(messages);
        // 每个文件的检测结果中的messages数组，每个message对应一个规则的检测结果
        if (isValuableArray(messages)) {
          messages.forEach(messageItem => {
            const { ruleId, message } = messageItem;
            // console.log(ruleId, message);
            const ruleItem = rules.find(item => item.ruleId === ruleId);
            if (ruleItem) {
              // console.log(`ruleMsg: ${ruleItem.ruleMsg}`);
              Report.formatAndpushReportData(
                [{
                  fileName: vueItem.fileName,
                  filePath: vueItem.filePath,
                  warnMsg: escapeHTML(message)
                }],
                escapeHTML(ruleItem.ruleMsg),
                outputList
              );
            }
          });
        }
      });
    }
  }
}
module.exports = FwVue;
